#include "uif.h"

int main() {
    struct ifi_info *ifi, *ifihead;
    struct sockaddr_in *sa;
    int i;
    char addrstr[INET_ADDRSTRLEN];
    ifihead = ifi = get_ifi_info();

    while(ifi) {
        printf("%s: ", ifi->ifi_name);
        printf("(%d) ", ifi->ifi_index);

        printf("<");
        if (ifi->ifi_flags & IFF_UP) printf("UP ");
        if (ifi->ifi_flags & IFF_BROADCAST) printf("BCAST ");
        if (ifi->ifi_flags & IFF_MULTICAST) printf("MCAST ");
        if (ifi->ifi_flags & IFF_LOOPBACK) printf("LOOP ");
        if (ifi->ifi_flags & IFF_POINTOPOINT) printf("P2P ");
        printf(">\n");

        if(ifi->ifi_hlen) {
            printf(" MAC: ");
            for(i=0; i < 6; i++){
                if(i==5){
                    printf("%02x\n", ifi->ifi_haddr[i]);
                } else {
                    printf("%02x:", ifi->ifi_haddr[i]);
                }
            }
        }

        if(ifi->ifi_metric != 0) {
            printf(" METRIC: %u\n", ifi->ifi_metric);
        }

        if(ifi->ifi_qlen != 0) {
            printf(" qlen: %u\n", ifi->ifi_qlen);
        }

        if(ifi->ifi_mtu != 0) {
            printf(" MTU: %u\n", ifi->ifi_mtu);
        }
        if(ifi->ifi_addr != NULL) {
            sa = (struct sockaddr_in *)ifi->ifi_addr;
            printf(" addr: %s\n", inet_ntop(AF_INET, &sa->sin_addr, addrstr, 16));
        }

        if(ifi->ifi_brdaddr != NULL) {
            sa = (struct sockaddr_in *)ifi->ifi_brdaddr;
            printf(" broadcast addr: %s\n", inet_ntop(AF_INET, &sa->sin_addr, addrstr, 16));
        }

        if(ifi->ifi_dstaddr != NULL) {
            sa = (struct sockaddr_in *)ifi->ifi_dstaddr;
            printf(" destination addr: %s\n", inet_ntop(AF_INET, &sa->sin_addr, addrstr, 16));
        }

        if(ifi->ifi_netmask != NULL) {
            sa = (struct sockaddr_in *)ifi->ifi_netmask;
            printf(" netmask: %s\n", inet_ntop(AF_INET, &sa->sin_addr, addrstr, 16));
        }

        ifi = ifi->ifi_next;
    }
    free_ifi_info(ifihead);
    return 0;
}

/*
* 获取接口信息
*/
struct ifi_info * get_ifi_info() {
    struct ifi_info *ifi, *ifihead, **ifipnext;
    int sockfd, len, lastlen, flags;
    char *buf, *ptr, lastname[IFNAMSIZ];
    struct ifconf ifc;
    struct ifreq *ifr, ifrcopy;
    struct sockaddr_in *sinptr;

    if (-1 == (sockfd = socket(AF_INET, SOCK_DGRAM, 0))) {
        perror("socket");
        exit(-1);
    }
    lastlen = 0;
    len = 100 * sizeof(struct ifreq);
    for (; ;) {
        buf = (char *)malloc(len);
        ifc.ifc_len = len;
        ifc.ifc_buf = buf;
        if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) {
            if (errno != EINVAL || lastlen != 0) {
                perror("ioctl");
                exit(-1);
            }
        } else {
            if (ifc.ifc_len == lastlen) {
                break;
            }
            lastlen = ifc.ifc_len;
        }
        len += 10 * sizeof(struct ifreq);
        free(buf);
    }
    ifihead = NULL;
    ifipnext = &ifihead;
    lastname[0] = 0;


    for (ptr = buf; ptr < buf + ifc.ifc_len; ) {
        ifr = (struct ifreq *) ptr;
        
        len = sizeof(struct ifreq);

        ptr += len;  // 下一个开始的位置

        if ( ifr->ifr_addr.sa_family != AF_INET) {
            continue;
        }

        if (strncmp(lastname, ifr->ifr_name, IFNAMSIZ) == 0) {
            continue;
        }

        memcpy(lastname, ifr->ifr_name, IFNAMSIZ);

        ifrcopy = *ifr;

        // 请求得到flags
        ioctl(sockfd, SIOCGIFFLAGS, &ifrcopy);

        flags = ifrcopy.ifr_flags;  
        
        if ((flags & IFF_UP) == 0) continue;

        ifi = (struct ifi_info *)malloc(sizeof(struct ifi_info));
        *ifipnext = ifi;
        ifipnext = &ifi -> ifi_next;

        ifi->ifi_flags = flags;
        ifi->ifi_myflags = 0;
#if defined(SIOCGIFMTU)
        ioctl(sockfd, SIOCGIFMTU, &ifrcopy);
        ifi->ifi_mtu = ifrcopy.ifr_mtu;
#else
        ifi->ifi_mtu = 0;
#endif
        memcpy(ifi->ifi_name, ifr->ifr_name, IFI_NAME_LEN);
        ifi->ifi_name[IFI_NAME_LEN - 1] = '\0';

#if defined(SIOCGIFINDEX)
        ioctl(sockfd, SIOCGIFINDEX, &ifrcopy);
        ifi->ifi_index = ifrcopy.ifr_ifindex;
#else
        ifi->ifi_index = 0;
#endif

#ifdef SIOCGIFTXQLEN
        ioctl(sockfd, SIOCGIFTXQLEN, &ifrcopy);
        ifi->ifi_qlen = (uint16_t)ifrcopy.ifr_qlen;
#else
        ifi->ifi_qlen = 0;
#endif

#ifdef  SIOCGIFMETRIC
        ioctl(sockfd, SIOCGIFMETRIC, &ifrcopy);
        ifi->ifi_metric = (uint16_t)ifrcopy.ifr_metric;
#else
        ifi->ifi_metric = 0;
#endif
        // 获取mac地址
#if defined(SIOCGIFHWADDR) 
        if ( ioctl(sockfd, SIOCGIFHWADDR, &ifrcopy) == 0 ) {
            memcpy(ifi->ifi_haddr, ifrcopy.ifr_hwaddr.sa_data, 6);
            ifi->ifi_hlen = 6;
        }
#else
        ifi->ifi_hlen = 0;
#endif


        sinptr = (struct sockaddr_in *) &ifr->ifr_addr;
        ifi->ifi_addr = (struct sockaddr *)malloc(sizeof(struct sockaddr));
        memcpy(ifi->ifi_addr, sinptr, sizeof(struct sockaddr_in));


#ifdef SIOCGIFNETMASK
        ioctl(sockfd, SIOCGIFNETMASK, &ifrcopy);
        sinptr = (struct sockaddr_in *) &ifrcopy.ifr_netmask;
        ifi->ifi_netmask = (struct sockaddr *)malloc(sizeof(struct sockaddr));
        memcpy(ifi->ifi_netmask, sinptr, sizeof(struct sockaddr_in));
#endif


        if(flags & IFF_BROADCAST) {
            ioctl(sockfd, SIOCGIFBRDADDR, &ifrcopy);
            sinptr = (struct sockaddr_in *) &ifrcopy.ifr_broadaddr;
            ifi->ifi_brdaddr = (struct sockaddr *)malloc(sizeof(struct sockaddr));
            memcpy(ifi->ifi_brdaddr, sinptr, sizeof(struct sockaddr_in));
        }

        if(flags & IFF_POINTOPOINT) {
            ioctl(sockfd, SIOCGIFDSTADDR, &ifrcopy);
            sinptr = (struct sockaddr_in *) &ifrcopy.ifr_dstaddr;
            ifi->ifi_dstaddr = (struct sockaddr *)malloc(sizeof(struct sockaddr));
            memcpy(ifi->ifi_dstaddr, sinptr, sizeof(struct sockaddr_in));
        }
    }
    free(buf);
    close(sockfd);
    return ifihead;
}

void free_ifi_info(struct ifi_info *ifihead) {
    struct ifi_info *ifi, *ifinext;
    for (ifi = ifihead; ifi != NULL; ifi=ifinext) {
        if (ifi->ifi_addr != NULL) {
            free(ifi->ifi_addr);
        }
        if (ifi->ifi_brdaddr != NULL) {
            free(ifi->ifi_brdaddr);
        }
        if (ifi->ifi_dstaddr != NULL) {
            free(ifi->ifi_dstaddr);
        }
        ifinext = ifi->ifi_next;
        free(ifi);
    }
}
